package main

import (
	"fmt"
	"flag"
	"crypto/blowfish"
	"crypto/cipher"
	"os"
	"io"
)

var pass *string = flag.String("pwd", "", "Your secret password")
var ifname *string = flag.String("if", "", "Input file, default to stdin")
var ofname *string = flag.String("of", "", "Output file, default to stdout")
var reverse *bool = flag.Bool("rev", false, "Make the reverse process, \nRead from crypted \"if\" and output plain in \"of\"")

func make_iv(sz int) []byte {
	ret := make([]byte, sz)
	for i := 0; i < sz; i++ {
		ret[i] = byte(i * 2 % 255)
	}
	return ret
}

func openFiles(ifn, ofn string) (_if *os.File, _of *os.File, err os.Error) {

	if len(ifn) == 0 {
		fmt.Fprintf(os.Stderr, "Using stdin as input\n")
		_if = os.Stdin
	} else {
		_if, err = os.Open(ifn)
		if err != nil {
			_if = nil
		}
	}

	if len(ofn) == 0 {
		fmt.Fprintf(os.Stderr, "Using stdout as output\n")
		_of = os.Stdout
	} else {
		_of, err = os.Create(ofn)
		if err != nil {
			_of = nil
		}
	}

	return
}

func main() {
	flag.Parse()

	if len(*pass) == 0 {
		fmt.Fprintf(os.Stderr, "The password is required.\n")
		flag.PrintDefaults()
		return
	}

	c, err := blowfish.NewCipher([]byte(*pass))

	if err != nil {
		fmt.Fprintf(os.Stderr, "Error creating the blowfish cipher: %s\n", err)
		return
	}
	s := cipher.NewOFB(c, make_iv(c.BlockSize()))

	if s == nil {
		fmt.Fprintf(os.Stderr, "Unable to create OFB block cipher\n")
	}

	_if, _of, err := openFiles(*ifname, *ofname)

	if _if == nil {
		fmt.Fprintf(os.Stderr, "Unable to open input file\n")
		return
	}

	if _of == nil {
		fmt.Fprintf(os.Stderr, "Unable to open output file\n")
		return
	}

	defer _if.Close()
	defer _of.Close()

	var dataCopied int64

	if *reverse {
		sr := &cipher.StreamReader{s, _if}
		dataCopied, err = io.Copy(_of, sr)
	} else {
		sw := &cipher.StreamWriter{s, _of, nil}
		dataCopied, err = io.Copy(sw, _if)
	}

	fmt.Fprintf(os.Stderr, "Input file was: %s\n", *ifname)
	fmt.Fprintf(os.Stderr, "Output file was: %s\n", *ofname)
	fmt.Fprintf(os.Stderr, "Total data copied: %d\n", dataCopied)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %s\n", err)
	}
}
